#pragma once
#ifndef __PLATFORM_ASSERT_H__
#define __PLATFORM_ASSERT_H__
#include <platform_preprocessor.h>
#include <app_error.h>

#if defined(__cplusplus) && (__cplusplus >= 201103L || __cpp_static_assert >= 200410L)
#define PLATFORM_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
#elif !defined(__cplusplus) && __STDC_VERSION__ >= 201112L
#define PLATFORM_STATIC_ASSERT(expr, msg) _Static_assert(expr, msg)
#elif defined(__cplusplus) && defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 403L
#define PLATFORM_STATIC_ASSERT(expr, msg) __extension__ static_assert(expr, msg)
#elif !defined(__cplusplus) && defined(__GNUC__) && !defined(__CC_ARM) && (__GNUC__ * 100 + __GNUC_MINOR__) > 406L
#define PLATFORM_STATIC_ASSERT(expr, msg) __extension__ _Static_assert(expr, msg)
#elif defined(__ICCARM__)
#define PLATFORM_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
#else
#define PLATFORM_STATIC_ASSERT(expr, msg)                                                                                                                                                              \
    enum                                                                                                                                                                                               \
    {                                                                                                                                                                                                  \
        PLATFORM_CONCAT(PLATFORM_ASSERTION_AT_, __LINE__) = sizeof(char[(expr) ? 1 : -1])                                                                                                              \
    }
#endif

#include <stdlib.h>
//
// There are two configuration options for this implementation's behavior
// when pre/post conditions on the GSL types are violated:
//
// 1. TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default)
// 2. UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens
//

//#define TERMINATE_ON_CONTRACT_VIOLATION
#define UNENFORCED_ON_CONTRACT_VIOLATION

#if defined(__clang__) || defined(__GNUC__)
#define PLATFORM_LIKELY(x) __builtin_expect(!!(x), 1)
#define PLATFORM_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define PLATFORM_LIKELY(x) (!!(x))
#define PLATFORM_UNLIKELY(x) (!!(x))
#endif

//
// PLATFORM_ASSUME(cond)
//
// Tell the optimizer that the predicate cond must hold. It is unspecified
// whether or not cond is actually evaluated.
//
#ifdef _MSC_VER
#define PLATFORM_ASSUME(cond) __assume(cond)
#elif defined(__GNUC__)
//#define PLATFORM_ASSUME(cond) ((cond) ? (void)(0) : __builtin_unreachable())
#define PLATFORM_ASSUME(cond) (void)((cond) ? 0 : 0)
#else
#define PLATFORM_ASSUME(cond) (void)((cond) ? 0 : 0)
#endif

extern void test_loop(void);
#if defined(TERMINATE_ON_CONTRACT_VIOLATION)
#define PLATFORM_CONTRACT_CHECK(type, cond) (PLATFORM_LIKELY(cond) ? (void)(0) : test_loop())
#elif defined(UNENFORCED_ON_CONTRACT_VIOLATION)
#define PLATFORM_CONTRACT_CHECK(type, cond) PLATFORM_ASSUME(cond)
#endif //!<#if defined(TERMINATE_ON_CONTRACT_VIOLATION)

#define Expects(cond) PLATFORM_CONTRACT_CHECK("Precondition", cond)
#define Ensures(cond) PLATFORM_CONTRACT_CHECK("Postcondition", cond)

/** PLATFORM_STATIC_ASSERT
 *  Declare compile-time assertions, results in compile-time error if condition is false
 *
 *  The assertion acts as a declaration that can be placed at file scope, in a
 *  code block (except after a label), or as a member of a C++ class/struct/union.
 *
 *  @note
 *  Use of PLATFORM_STATIC_ASSERT as a member of a struct/union is limited:
 *  - In C++, PLATFORM_STATIC_ASSERT is valid in class/struct/union scope.
 *  - In C, PLATFORM_STATIC_ASSERT is not valid in struct/union scope, and
 *    PLATFORM_STRUCT_STATIC_ASSERT is provided as an alternative that is valid
 *    in C and C++ class/struct/union scope.
 *
 *  @code
 *  PLATFORM_STATIC_ASSERT(PLATFORM_LIBRARY_VERSION >= 120,
 *          "The mbed library must be at least version 120");
 *
 *  int main() {
 *      PLATFORM_STATIC_ASSERT(sizeof(int) >= sizeof(char),
 *              "An int must be larger than a char");
 *  }
 *  @endcode
 */
#if defined(__cplusplus) && (__cplusplus >= 201103L || __cpp_static_assert >= 200410L)
#define PLATFORM_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
#elif !defined(__cplusplus) && __STDC_VERSION__ >= 201112L
#define PLATFORM_STATIC_ASSERT(expr, msg) _Static_assert(expr, msg)
#elif defined(__cplusplus) && defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 403L
#define PLATFORM_STATIC_ASSERT(expr, msg) __extension__ static_assert(expr, msg)
#elif !defined(__cplusplus) && defined(__GNUC__) && !defined(__CC_ARM) && (__GNUC__ * 100 + __GNUC_MINOR__) > 406L
#define PLATFORM_STATIC_ASSERT(expr, msg) __extension__ _Static_assert(expr, msg)
#elif defined(__ICCARM__)
#define PLATFORM_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
#else
#define PLATFORM_STATIC_ASSERT(expr, msg)                                                                                                                                                              \
    enum                                                                                                                                                                                               \
    {                                                                                                                                                                                                  \
        PLATFORM_CONCAT(PLATFORM_ASSERTION_AT_, __LINE__) = sizeof(char[(expr) ? 1 : -1])                                                                                                              \
    }
#endif

/** PLATFORM_STRUCT_STATIC_ASSERT
 *  Declare compile-time assertions, results in compile-time error if condition is false
 *
 *  Unlike PLATFORM_STATIC_ASSERT, PLATFORM_STRUCT_STATIC_ASSERT can and must be used
 *  as a member of a C/C++ class/struct/union.
 *
 *  @code
 *  struct thing {
 *      PLATFORM_STATIC_ASSERT(2 + 2 == 4,
 *              "Hopefully the universe is mathematically consistent");
 *  };
 *  @endcode
 */
#define PLATFORM_STRUCT_STATIC_ASSERT(expr, msg) int : (expr) ? 0 : -1

#endif //!<__PLATFORM_ASSERT_H__
